const app = getApp()
const common = require('../common');
const utils = require('../../utils/util.js');

const conversationTypes = [
  { type: 1, label: '单聊' },
  { type: 3, label: '群聊' }
];

const Status = {
  // 空闲
  FREE: 0,
  // 等待接听或挂断，暂未加入房间，包含呼入、呼出场景
  WAITING: 1,
  // 通话中，即已经加入房间，群聊状态下有一人曾接通过即进入到通话中状态
  TALKING: 2,
}

// 该定义为 CallLib 中的媒体类型类型定义，非 RTCLib 层的定义
// RTCLib 中的定义未 0 音频 1 视频 2 音视频
const MediaTypes = [
  { type: 2, label: '视频通话' },
  { type: 1, label: '音频通话' },
];

Page({
  data: {
    crtUserId: '',

    // 通话状态
    status: Status.FREE,
    // 根据 status 变化修改，computed
    free: true,
    waiting: false,
    talking: false,
    // 群呼中途邀请标记
    inviting: false,
    // 被邀请人
    inviteUserIds: '',
    // 是否为呼入
    callin: false,
    // 当前通话的 targetId，单聊为对方 userId，群聊为 groupId
    crtTargetId: '',
    // 当前的通话类型
    crtConversationType: 0,
    // 当前通话中的人员，不包含自己，单聊时长度为 1，群聊时长度 >= 1，当长度为 0 时需挂断当前通话
    // 某些条件下，对方可能无法发送 hungupMessage 信息，如直接关闭应用、杀进程
    crtMembers: [],

    // 输入选项
    conversationTypeOptions: conversationTypes.map(item => item.label),
    mediaTypeOptions: MediaTypes.map(item => item.label),

    // 输入表单
    userId: '',
    groupId: '',
    conversationTypeIndex: 0,
    conversationType: conversationTypes[0].type,
    mediaTypeIndex: 0,
    mediaType: MediaTypes[0].type,

    // 推送视频流
    pusher: null,
    // 拉取视频流：{ [userId]: mediaStream }
    streams: {
    },
  },
  setStatus(status) {
    const free = status === Status.FREE;
    const data = {
      status,
      free,
      talking: status === Status.TALKING,
      waiting: status === Status.WAITING,
    };
    if (free) {
      data.crtTargetId = '';
      data.crtConversationType = 0;
      data.crtMembers = [];
      data.pusher = null;
      data.streams = {};
    }
    this.setData(data);
  },
  setMediaTypeIndex(index) {
    this.setData({
      mediaTypeIndex: index,
      mediaType: MediaTypes[index].type
    })
  },
  inputUserId(e) {
    const userId = e.detail.value;
    this.setData({ userId });
  },
  inputGroupId(e) {
    const groupId = e.detail.value;
    this.setData({ groupId });
  },
  inputConversationTypeChange(e) {
    const conversationTypeIndex = parseInt(e.detail.value);
    this.setData({
      conversationTypeIndex,
      conversationType: conversationTypes[conversationTypeIndex].type
    })
  },
  inputMediaTypeChange(e) {
    const index = parseInt(e.detail.value);
    this.setMediaTypeIndex(index);
  },
  async call() {
    const { Call, ConversationType } = app.getService();
    const { conversationType, groupId, userId, mediaType, crtUserId } = this.data;
    const isGroup = conversationType === ConversationType.GROUP;

    if (!userId) {
      common.showToast('userId is required');
      return;
    }
    if (isGroup && !groupId) {
      common.showToast('groupId is required');
      return;
    }

    const targetId = isGroup ? groupId : userId;
    const userIds = isGroup ? userId.replace(/\s/g, '').split(',').filter(item => item.length > 0) : [userId];

    if (targetId === crtUserId || userIds.includes(crtUserId)) {
      common.showToast("can't call yourself");
      return;
    }
    
    wx.showLoading({ title: '正在呼叫...', mask: true })

    this.setData({
      crtConversationType: conversationType,
      crtTargetId: targetId,
      callin: false,
    })
    this.setMembers(userIds);
    this.setStatus(Status.WAITING);

    try {
      await Call.call({
        conversationType,
        targetId,
        inviteUserIds: userIds,
        mediaType,
      });
    } catch(error) {
      wx.hideLoading();
      console.warn('call ->', error);
      common.showToast(`呼叫失败: ${error}`);
      this.hungup();
      return;
    }
    wx.hideLoading();
    common.showToast('已发出呼叫');
  },
  async accept() {
    wx.showLoading({ title: '正在接听...', mask: true });
    const {
      crtConversationType: conversationType,
      crtTargetId: targetId,
      crtMembers,
      mediaType
    } = this.data;
    const { Call } = app.getService();
    try {
      await Call.accept({
        conversationType,
        targetId,
        inviteUserIds: crtMembers.slice(),
        mediaType,
      });
    } catch (error) {
      console.log(error);
      wx.hideLoading();
      common.showToast(`接听失败：${error}`);
      this.hungup();
      return;
    }
    wx.hideLoading();
    this.setStatus(Status.TALKING);
  },
  showInvite() {
    this.setData({ inviting: true, inviteUserIds: '' });
  },
  hideInvite() {
    this.setData({ inviting: false, inviteUserIds: '' });
  },
  inputInviteUserId(e) {
    const userId = e.detail.value;
    this.setData({
      inviteUserIds: userId
    });
  },
  async invite () {
    const {
      crtTargetId: targetId,
      crtConversationType: conversationType,
      mediaType,
      inviteUserIds,
      crtUserId,
    } = this.data;

    const userIds = inviteUserIds.replace(/\s/g, '').split(',').filter(item => item.length > 0);
    if (userIds.length === 0) {
      common.showToast('lost params');
      return;
    }

    if (userIds.includes(crtUserId)) {
      common.showToast("can't invite yourself");
      return;
    }

    wx.showLoading({ title: '正在邀请...', mask: true })

    try {
      await app.getService().Call.invite({
        conversationType,
        targetId,
        inviteUserIds: inviteUserIds.split(','),
        mediaType,
      });
    } catch(error) {
      wx.hideLoading();
      console.log('invite -> ', error);
      common.showToast(`邀请失败: ${error}`);
      return;
    }

    // 更新用户列表
    const { crtMembers } = this.data;
    this.setMembers([...crtMembers, ...userIds]);

    this.hideInvite();
    wx.hideLoading();
    common.showToast('已发出邀请');
  },
  setMembers(userIds) {
    console.info('setMembers:', userIds);
    // 剔除自己及重复 ID，CallLib 内部 bug 状态错误可能导致有重复 id
    const { crtUserId } = this.data;
    const crtMembers = [];
    userIds.forEach(userId => {
      if (userId === crtUserId) {
        return;
      }
      if (crtMembers.includes(userId)) {
        return;
      }
      crtMembers.push(userId);
    })
    this.setData({ crtMembers });
  },
  removeMember(userId) {
    console.warn('removeMember called', userId);
    const { crtMembers, streams } = this.data;
    
    const tmpArr = crtMembers.filter(item => item !== userId);
    if (tmpArr.length === 0) {
      this.hungup();
      return;
    }

    delete streams[userId];
    this.setData({ streams });
    this.setMembers(tmpArr);
  },
  async hungup () {
    console.warn('hungup called');
    wx.showLoading({
      title: '正在结束...',
      mask: true,
    })
    const {
      crtConversationType: conversationType,
      crtTargetId: targetId,
    } = this.data;
    try {
      await app.getService().Call.hungup({
        conversationType,
        targetId,
      });
    } catch (error) {
      console.error('挂断失败', error);
    }
    this.setStatus(Status.FREE);
    this.hideInvite();
    wx.hideLoading();
  },
  async onLoad(options) {
    const { userId: crtUserId, appkey } = options;
    this.setData({ crtUserId });
    const token = decodeURIComponent(options.token);

    wx.setNavigationBarTitle({
      title: `${crtUserId} - ${appkey}`
    })

    const service = app.getService();
    const { Connection, Call } = service;

    wx.showLoading({ title: 'IM 连接中...', mask: true });

    // 监听连接变化
    Connection.watch((status) => {
      console.log('status:', status);
    });

    try {
      await Connection.connect({ token });
    } catch(err) {
      wx.hideLoading();
      common.showToast('IM 连接失败');
      wx.navigateBack();
      return;
    }

    wx.hideLoading();
    common.showToast('IM 连接成功');

    Call.watchCommand(this.onReceiveMessage.bind(this));
    Call.watchVideo(this.onVideoChange.bind(this));
  },
  onVideoChange(video) {
    const { type, userId, data } = video;
    const { streams } = this.data;
    console.log('onVideoChange =>', type, userId);

    switch(type) {
      case 'removed':
        this.removeMember(userId);
        break;
      case 'added':
        if (video.isLocal) {
          this.setData({ pusher: video });
          return;
        }
        streams[userId] = data;
        this.setData({ streams });
        break;
    }
  },
  onReceiveMessage(message) {
    const { Call, ConversationType } = app.getService();
    const { messageType, conversationType, targetId, senderUserId, content } = message;
    const MessageType = Call.MessageType

    console.info('onReceiveMessage:', messageType, targetId, senderUserId, content);
    common.showToast(messageType);

    switch(messageType) {
      case MessageType.AcceptMessage:
        this.setStatus(Status.TALKING);
        break;
      case MessageType.SummaryMessage:
        this.setStatus(Status.FREE);
        break;
      case MessageType.InviteMessage:
        this.setData({
          callin: true,
          crtTargetId: targetId,
          crtConversationType: conversationType,
        });
        const userIds = [senderUserId, ...content.inviteUserIds];
        this.setMembers(userIds);
        const index = MediaTypes.findIndex(item => item.type === content.mediaType);
        this.setMediaTypeIndex(index);
        this.setStatus(Status.WAITING);
        break;
      case MessageType.HungupMessage:
        if (conversationType === ConversationType.PRIVATE) {
          this.hungup();
          return;
        }
        this.removeMember(senderUserId);
        break;
      case MessageType.MemberModifyMessage:
        const { crtMembers, free } = this.data;
        const { inviteUserIds, existedMemberStatusList } = content;

        // 被中途邀请进房间
        if (free) {
          const index = MediaTypes.findIndex(item => item.type === content.mediaType);
          this.setMediaTypeIndex(index);
          this.setData({
            callin: true,
            crtTargetId: targetId,
            crtConversationType: conversationType,
          });
          this.setMembers([...inviteUserIds, ...existedMemberStatusList.map(item => item.userId)]);
          this.setStatus(Status.WAITING);
          return;
        }
        // 有其他人被邀请进入房间
        this.setMembers([...crtMembers, ...inviteUserIds]);
        break;
    }
  },
  async onUnload () {
    const { Connection, Call } = app.getService();
    await this.hungup();
    Call.destroy();
    Connection.disconnect();
  },
  statechange(e) {
    console.log('live-pusher code:', e.detail.code)
  }
});